/*
 *
 * Copyright (c) 2020-2022, Java知识图谱 (http://www.altitude.xin).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package xin.altitude.db.redis.sync.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xin.altitude.cms.common.util.RedisUtils;
import xin.altitude.db.redis.sync.domain.BuOrder;
import xin.altitude.db.redis.sync.mapper.OrderMapper;
import xin.altitude.db.redis.sync.service.IOrderService;
import xin.altitude.db.redis.sync.utils.LockOptional;

import java.util.concurrent.TimeUnit;

/**
 * @author explore
 * @since 2022/03/04 19:07
 **/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, BuOrder> implements IOrderService {
    public static final String ORDER_KEY_PREFIX = "ORDER_KEY_PREFIX::";
    public static final String ORDER_LOCK = "order_lock::";
    
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    /**
     * 编辑订单
     *
     * @param order
     * @return
     */
    @Override
    public Boolean editOrder(BuOrder order) {
        String orderLock = ORDER_LOCK + order.getOrderId();
        RLock lock = redissonClient.getLock(orderLock);
        try {
            /* 超时未获取到锁，快速失败，用户端重试 */
            if (lock.tryLock(1, TimeUnit.SECONDS)) {
                /* 更新数据库 */
                updateById(order);
                /* 删除缓存 */
                RedisUtils.deleteObject(OrderServiceImpl.ORDER_KEY_PREFIX + order.getOrderId());
                /* 释放锁 */
                LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
                return true;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
    
    /**
     * 1. 使用分布式行锁提高并发量
     * 2. 使用二次检查机制，确保等待获得锁的线程能够快速返回结果
     * 查询订单详情
     *
     * @param orderId 订单ID
     * @return BuOrder
     */
    @Override
    public BuOrder getOrder(Long orderId) {
        /* 如果缓存不存在，则添加分布式锁更新缓存 */
        String key = ORDER_KEY_PREFIX + orderId;
        BuOrder order = RedisUtils.getObject(key, BuOrder.class);
        if (order != null) {
            return order;
        }
        String orderLock = ORDER_LOCK + orderId;
        RLock lock = redissonClient.getLock(orderLock);
        if (lock.tryLock()) {
            order = RedisUtils.getObject(key, BuOrder.class);
            if (order != null) {
                LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
                return order;
            }
            BuOrder buOrder = getById(orderId);
            RedisUtils.setObject(key, buOrder, 5, TimeUnit.MINUTES);
            LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
        }
        return RedisUtils.getObject(key, BuOrder.class);
    }
}
